/*

    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+


Questions are always Name, Type, Class tuples. For Internet applications, the Class is IN, the Type is a valid RR type, and the Name is a fully-qualified domain name, stored in a standard format. Names can't be wildcarded, but Types and Classes can be. In addition, special Types exist to wildcard mail records and to trigger zone transfers. The question is the only section included in a query message; the remaining sections being used for replies. 

Answers are RRs that match the Name, Type, Class tuple. If any of the matching records are CNAME pointers leading to other records, the target records should also be included in the answer. There may be multiple answers, since there may be multiple RRs with the same labels. 

Authority RRs are type NS records pointing to name servers closer to the target name in the naming hierarchy. This field is completely optional, but clients are encouraged to cache this information if further requests may be made in the same name hierarchy. 

Additional RRs are records that the name server believes may be useful to the client. The most common use for this field is to supply A (address) records for the name servers listed in the Authority section. 
However, more clever name servers are feasible. For example, if the question is for an MX record for FreeSoft.org, the answer will currently point to mail.adnc.com. The name server can infer that the client's next request will be an A query for mail.adnc.com, which will be answered by with a CNAME record, the DNS equivalent of a symbolic link, and the target of that link, an A record for gemini.adnc.com. The name server can avoid all this extra traffic by just including the CNAME and A records as additional RRs in the original reply. Not all name servers do this, however. Use the Dig program to watch what really happens. 

*/

#include <xp/net/dns.h>
#include <xp/net/inet.h>
#include <xp/bas/memory.h>
#include <xp/bas/string.h>
#include <xp/bas/time.h>

xp_dns_t* xp_dns_open (xp_dns_t* dns)
{
	xp_time_t now;

	if (dns == XP_NULL) {
		dns = (xp_dns_t*)xp_malloc (xp_sizeof(xp_dns_t));
		if (dns == XP_NULL) return XP_NULL;
		dns->__dynamic = xp_true;
	}
	else dns->__dynamic = xp_false;

	dns->handle = xp_sckopen (AF_INET, SOCK_DGRAM, 0);
	if (dns->handle == XP_SCKHND_INVALID) {
		if (dns->__dynamic) xp_free (dns);
		return XP_NULL;
	}

	if (xp_gettime(&now) == -1) dns->count = 0;
	else dns->count = (xp_size_t)now;

	return dns;
}

int xp_dns_close (xp_dns_t* dns)
{
	if (xp_sckclose (dns->handle) == -1) return -1;
	if (dns->__dynamic) xp_free (dns);
	return 0;
}

int xp_dns_query_a (xp_dns_t* dns, const xp_char_t* name)
{
	xp_byte_t buf[65535];	
	xp_dns_header_t* hdr = (xp_dns_header_t*)buf;
	xp_byte_t* nbuf;
	xp_size_t len, name_len;
	xp_uint16_t* tmp;
	xp_sckadrin4_t sin;
	xp_uint32_t ip;

	xp_memset (buf, 0, xp_sizeof(buf));
	hdr->id = xp_hton16(dns->count++);
#if 0
	hdr->flags.qr = 0; /* query */
	hdr->flags.opcode = 0; /* standard query */
	hdr->flags.rd = 1; /* recursion desired */
#endif
	hdr->flags = xp_hton16 (0x0100); /* standard query */
	hdr->qncount = xp_hton16(1); /* number of question */
	
	nbuf = buf + xp_sizeof(xp_dns_header_t);
	name_len = xp_strtodn (name, nbuf,
		xp_sizeof(buf) - xp_sizeof(xp_dns_header_t));
	tmp = (xp_uint16_t*)(nbuf + name_len);
	*tmp++ = xp_hton16(1);
	*tmp = xp_hton16(1);

	len = xp_sizeof(xp_dns_header_t) + name_len + xp_sizeof(*tmp) * 2;

	xp_memset (&sin, 0, xp_sizeof(sin));
	xp_inet4_strtoip (XP_TEXT("192.168.123.100"), &ip);
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = ip;
	sin.sin_port = xp_hton16(53);
	if (xp_scksendto (dns->handle, buf, len, 0, 
		(xp_sckadr_t*)&sin, xp_sizeof(sin), -1) == -1) return -1;

	return 0;
}
